﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Storage.Streams;

namespace Windows.Storage
{
    /// <summary>
    /// Provides helper methods for reading and writing a file using the absolute path or Uniform Resource Identifier (URI) of the file.
    /// </summary>
    public static class PathIO
    {
        /// <summary>
        /// Reads the contents of the file at the specified path or Uniform Resource Identifier (URI) and returns text.
        /// </summary>
        /// <param name="absolutePath">The path of the file to read.</param>
        /// <returns>When this method completes successfully, it returns the contents of the file as a text string.</returns>
        public static IAsyncOperation<string> ReadTextAsync(string absolutePath)
        {
            return ReadTextAsyncInternal(absolutePath).AsAsyncOperation();
        }

        /// <summary>
        /// Reads the contents of the file at the specified path or Uniform Resource Identifier (URI) using the specified character encoding and returns text.
        /// </summary>
        /// <param name="absolutePath">The path of the file to read.</param>
        /// <param name="encoding">The character encoding of the file.</param>
        /// <returns>When this method completes successfully, it returns the contents of the file as a text string.</returns>
        public static IAsyncOperation<string> ReadTextAsync(string absolutePath, UnicodeEncoding encoding)
        {
            return ReadTextAsyncInternal(absolutePath, encoding).AsAsyncOperation();
        }

        /// <summary>
        /// Writes text to the file at the specified path or Uniform Resource Identifier (URI).
        /// </summary>
        /// <param name="absolutePath">The path of the file that the text is written to.</param>
        /// <param name="contents">The text to write.</param>
        /// <returns>No object or value is returned when this method completes.</returns>
        public static IAsyncAction WriteTextAsync(string absolutePath, string contents)
        {
            return WriteTextAsyncInternal(absolutePath, contents).AsAsyncAction();
        }

        /// <summary>
        /// Writes text to the file at the specified path or Uniform Resource Identifier (URI) using the specified character encoding.
        /// </summary>
        /// <param name="absolutePath">The path of the file that the text is written to.</param>
        /// <param name="contents">The text to write.</param>
        /// <param name="encoding">The character encoding of the file.</param>
        /// <returns>No object or value is returned when this method completes.</returns>
        public static IAsyncAction WriteTextAsync(string absolutePath, string contents, UnicodeEncoding encoding)
        {
            return WriteTextAsyncInternal(absolutePath, contents, encoding).AsAsyncAction();
        }

        /// <summary>
        /// Appends text to the file at the specified path or Uniform Resource Identifier (URI).
        /// </summary>
        /// <param name="absolutePath">The path of the file that the text is appended to.</param>
        /// <param name="contents">The text to append.</param>
        /// <returns>No object or value is returned when this method completes.</returns>
        public static IAsyncAction AppendTextAsync(string absolutePath, string contents)
        {
            return AppendTextAsyncInternal(absolutePath, contents).AsAsyncAction();
        }

        /// <summary>
        /// Appends text to the file at the specified path or Uniform Resource Identifier (URI) using the specified character encoding.
        /// </summary>
        /// <param name="absolutePath">The path of the file that the text is appended to.</param>
        /// <param name="contents">The text to append.</param>
        /// <param name="encoding">The character encoding of the file.</param>
        /// <returns>No object or value is returned when this method completes.</returns>
        public static IAsyncAction AppendTextAsync(string absolutePath, string contents, UnicodeEncoding encoding)
        {
            return AppendTextAsyncInternal(absolutePath, contents, encoding).AsAsyncAction();
        }

        /// <summary>
        /// Reads the contents of the file at the specified path or Uniform Resource Identifier (URI) and returns lines of text.
        /// </summary>
        /// <param name="absolutePath">The path of the file to read.</param>
        /// <returns>When this method completes successfully, it returns the contents of the file as a list (type IVector) of lines of text. Each line of text in the list is represented by a String object.</returns>
        public static IAsyncOperation<IList<string>> ReadLinesAsync(string absolutePath)
        {
            return ReadLinesAsyncInternal(absolutePath).AsAsyncOperation();
        }

        /// <summary>
        /// Reads the contents of the file at the specified path or Uniform Resource Identifier (URI) using the specified character encoding and returns lines of text.
        /// </summary>
        /// <param name="absolutePath">The path of the file to read.</param>
        /// <param name="encoding">The character encoding of the file.</param>
        /// <returns>When this method completes successfully, it returns the contents of the file as a list (type IVector) of lines of text. Each line of text in the list is represented by a String object.</returns>
        public static IAsyncOperation<IList<string>> ReadLinesAsync(string absolutePath, UnicodeEncoding encoding)
        {
            return ReadLinesAsyncInternal(absolutePath, encoding).AsAsyncOperation();
        }

        /// <summary>
        /// Writes lines of text to the file at the specified path or URI.
        /// </summary>
        /// <param name="absolutePath">The path of the file that the lines are written to.</param>
        /// <param name="lines">The list of text strings to append as lines.</param>
        /// <returns>No object or value is returned when this method completes.</returns>
        public static IAsyncAction WriteLinesAsync(string absolutePath, IEnumerable<string> lines)
        {
            return WriteLinesAsyncInternal(absolutePath, lines).AsAsyncAction();
        }

        /// <summary>
        /// Writes lines of text to the file at the specified path or URI using the specified character encoding.
        /// </summary>
        /// <param name="absolutePath">The path of the file that the lines are written to.</param>
        /// <param name="lines">The list of text strings to append as lines.</param>
        /// <param name="encoding">The character encoding of the file.</param>
        /// <returns>No object or value is returned when this method completes.</returns>
        public static IAsyncAction WriteLinesAsync(string absolutePath, IEnumerable<string> lines, UnicodeEncoding encoding)
        {
            return WriteLinesAsyncInternal(absolutePath, lines, encoding).AsAsyncAction();
        }

        /// <summary>
        /// Appends lines of text to the file at the specified path or URI.
        /// </summary>
        /// <param name="absolutePath">The path or URI of the file that the lines are appended to.</param>
        /// <param name="lines">The list of text strings to append as lines.</param>
        /// <returns>No object or value is returned when this method completes.</returns>
        public static IAsyncAction AppendLinesAsync(string absolutePath, IEnumerable<string> lines)
        {
            return AppendLinesAsyncInternal(absolutePath, lines).AsAsyncAction();
        }

        /// <summary>
        /// Appends lines of text to the file at the specified path or URI using the specified character encoding.
        /// </summary>
        /// <param name="absolutePath">The path or URI of the file that the lines are appended to.</param>
        /// <param name="lines">The list of text strings to append as lines.</param>
        /// <param name="encoding">The character encoding of the file.</param>
        /// <returns>No object or value is returned when this method completes.</returns>
        public static IAsyncAction AppendLinesAsync(string absolutePath, IEnumerable<string> lines, UnicodeEncoding encoding)
        {
            return AppendLinesAsyncInternal(absolutePath, lines, encoding).AsAsyncAction();
        }

        /// <summary>
        /// Reads the contents of the file at the specified path or Uniform Resource Identifier (URI) and returns a buffer.
        /// </summary>
        /// <param name="absolutePath">The path of the file to read.</param>
        /// <returns>When this method completes, it returns an object (type IBuffer) that represents the contents of the file.</returns>
        public static IAsyncOperation<IBuffer> ReadBufferAsync(string absolutePath)
        {
            return ReadBufferAsyncInternal(absolutePath).AsAsyncOperation();
        }

        /// <summary>
        /// Writes data from a buffer to the file at the specified path or Uniform Resource Identifier (URI).
        /// </summary>
        /// <param name="absolutePath">The path of the file that the data is written to.</param>
        /// <param name="buffer">The buffer that contains the data to write.</param>
        /// <returns>No object or value is returned when this method completes.</returns>
        public static IAsyncAction WriteBufferAsync(string absolutePath, IBuffer buffer)
        {
            return WriteBufferAsyncInternal(absolutePath, buffer).AsAsyncAction();
        }

        /// <summary>
        /// Writes a single byte of data to the file at the specified path or Uniform Resource Identifier (URI).
        /// </summary>
        /// <param name="absolutePath">The path of the file that the byte is written to.</param>
        /// <param name="buffer">An array of bytes to write.</param>
        /// <returns>No object or value is returned when this method completes.</returns>
        public static IAsyncAction WriteBytesAsync(string absolutePath, byte[] buffer)
        {
            return WriteBytesAsyncInternal(absolutePath, buffer).AsAsyncAction();
        }

        #region Internal implementation

        internal static async Task<string> ReadTextAsyncInternal(string absolutePath)
        {
            IStorageFile file = await GetFileByAbsolutePath(absolutePath);
            return await FileIO.ReadTextAsyncInternal(file);
        }

        internal static async Task<string> ReadTextAsyncInternal(string absolutePath, UnicodeEncoding encoding)
        {
            IStorageFile file = await GetFileByAbsolutePath(absolutePath);
            return await FileIO.ReadTextAsyncInternal(file, encoding);
        }

        internal static async Task WriteTextAsyncInternal(string absolutePath, string contents)
        {
            IStorageFile file = await GetFileByAbsolutePath(absolutePath);
            await FileIO.WriteTextAsyncInternal(file, contents);
        }

        internal static async Task WriteTextAsyncInternal(string absolutePath, string contents, UnicodeEncoding encoding)
        {
            IStorageFile file = await GetFileByAbsolutePath(absolutePath);
            await FileIO.WriteTextAsyncInternal(file, contents, encoding);
        }

        internal static async Task AppendTextAsyncInternal(string absolutePath, string contents)
        {
            IStorageFile file = await GetFileByAbsolutePath(absolutePath);
            await FileIO.AppendTextAsyncInternal(file, contents);
        }

        internal static async Task AppendTextAsyncInternal(string absolutePath, string contents, UnicodeEncoding encoding)
        {
            IStorageFile file = await GetFileByAbsolutePath(absolutePath);
            await FileIO.AppendTextAsyncInternal(file, contents, encoding);
        }

        internal static async Task<IList<string>> ReadLinesAsyncInternal(string absolutePath)
        {
            IStorageFile file = await GetFileByAbsolutePath(absolutePath);
            return await FileIO.ReadLinesAsyncInternal(file);
        }

        internal static async Task<IList<string>> ReadLinesAsyncInternal(string absolutePath, UnicodeEncoding encoding)
        {
            IStorageFile file = await GetFileByAbsolutePath(absolutePath);
            return await FileIO.ReadLinesAsyncInternal(file, encoding);
        }

        internal static async Task WriteLinesAsyncInternal(string absolutePath, IEnumerable<string> lines)
        {
            IStorageFile file = await GetFileByAbsolutePath(absolutePath);
            await FileIO.WriteLinesAsyncInternal(file, lines);
        }

        internal static async Task WriteLinesAsyncInternal(string absolutePath, IEnumerable<string> lines, UnicodeEncoding encoding)
        {
            IStorageFile file = await GetFileByAbsolutePath(absolutePath);
            await FileIO.WriteLinesAsyncInternal(file, lines, encoding);
        }

        internal static async Task AppendLinesAsyncInternal(string absolutePath, IEnumerable<string> lines)
        {
            IStorageFile file = await GetFileByAbsolutePath(absolutePath);
            await FileIO.AppendLinesAsyncInternal(file, lines);
        }

        internal static async Task AppendLinesAsyncInternal(string absolutePath, IEnumerable<string> lines, UnicodeEncoding encoding)
        {
            IStorageFile file = await GetFileByAbsolutePath(absolutePath);
            await FileIO.AppendLinesAsyncInternal(file, lines, encoding);
        }

        internal static async Task<IBuffer> ReadBufferAsyncInternal(string absolutePath)
        {
            IStorageFile file = await GetFileByAbsolutePath(absolutePath);
            return await FileIO.ReadBufferAsyncInternal(file);
        }

        internal static async Task WriteBufferAsyncInternal(string absolutePath, IBuffer buffer)
        {
            IStorageFile file = await GetFileByAbsolutePath(absolutePath);
            await FileIO.WriteBufferAsyncInternal(file, buffer);
        }

        internal static async Task WriteBytesAsyncInternal(string absolutePath, byte[] buffer)
        {
            IStorageFile file = await GetFileByAbsolutePath(absolutePath);
            await FileIO.WriteBytesAsyncInternal(file, buffer);
        }

        internal const string AppDataUriScheme = "ms-appdata:///";
        internal const string AppResourcesUriScheme = "ms-appx:///";

        internal static async Task<IStorageFile> GetFileByAbsolutePath(string absolutePath)
        {
            if (string.IsNullOrWhiteSpace(absolutePath))
            {
                throw new ArgumentNullException("absolutePath");
            }

            // if the app uri starts with known Windows Uri scheme, use GetFileFromApplicationUriAsync
            if (absolutePath.StartsWith(AppDataUriScheme) || 
                absolutePath.StartsWith(AppResourcesUriScheme))
            {
                return await StorageFile.GetFileFromApplicationUriAsync(new Uri(absolutePath, UriKind.RelativeOrAbsolute));
            }

            // otherwise try to use the GetFileFromPathAsync method
            // note we are expecting full file path
            return await StorageFile.GetFileFromPathAsync(absolutePath);
        }

        #endregion
    }
}